var nanovm = {};
var timer = null;
var memsize = 128*256;
var speed = 1;
var n = 0;
var cycles = 0;
var cyclelimit = 0xFFFFFFFF;
var ncLog = alert;
var isfight = false;
var maxthread = 3;
var ncdebug = {};

Number.prototype.mod = function(n) { return ((this%n)+n)%n; }
function NanoInitVM()
{
	nanovm.prgm = new list;
	nanovm.currentprgm = null;
	nanovm.AddProgram = function(prgm) { this.prgm.add_tail(prgm); if(this.currentprgm === null) this.currentprgm = prgm; }
	nanovm.RemoveProgram = function(prgm) { if(this.currentprgm == prgm) this.currentprgm = this.currentprgm.prev; this.prgm.remove(prgm); if(isfight) SetPlayerOut(prgm.id); if(this.prgm.empty()) { NanoStopExecution(); ncLog('Execution stopped : aucun programme à exécuter'); } }
	nanovm.GetNextProgram = function() { if(this.prgm.empty()) return false; var prgm = this.currentprgm; this.currentprgm = this.prgm.next(this.currentprgm); return prgm; }
	
	NanoCreateMemory();
	NanoResize(0);
}

var colors = [{'color':'#5858FA', 'ipcolor':'#0000FF'}, {'color':'#FE642E', 'ipcolor':'#8A2908'}, {'color':'#FA5858', 'ipcolor':'#FF0000'}, {'color':'#2EFE2E', 'ipcolor':'#088A29'}, {'color':'#D358F7', 'ipcolor':'#8A0886'}, {'color':'#AAAAAA', 'ipcolor':'#000000'}, {'color':'#5FB404', 'ipcolor':'#38610B'}, {'color':'#2EFEF7', 'ipcolor':'#0A2229'}];
var currentcolor = 0;
function NanoInitVMExec(players)
{
	NanoInitVM();
	ncLog = function(a) {};
	isfight = true;
	cyclelimit = 1000000;
	for(var name in players)
	{
		var code = players[name].code;
		SetPlayerColor(players[name].id, colors[currentcolor]);
		var prgm = NanoCreateProgram(name, players[name].id, code, colors[currentcolor++]);
		var addr = parseInt(prgm.header.addr);
		prgm.Map(addr);
		prgm.ctx.e.SetIP(addr, true);
		nanovm.AddProgram(prgm);
	}
	nanovm.checkstop = function() { if(nanovm.prgm.is_singleton()) return false; return true; }
	nanovm.callback = function() { if(!nanovm.prgm.is_singleton()) alert('Exécution terminée.\nEgalité.'); else { var winner = nanovm.prgm.head().name; alert('Exécution terminée.\nVainqueur : ' + winner + '.'); } NanoDrawCycles(); }
}

function SetPlayerColor(id, color)
{
	var e = document.getElementById('ncvmp_' + id);
	e.style.backgroundColor = color.color;
}

function SetPlayerOut(id)
{
	var e = document.getElementById('ncvmp_' + id);
	e.style.textDecoration = 'line-through';
}

function NanoServerExec()
{
	GetPageContent('GET', 'nano/nanovm.php?exec=1', null, NanoCheckServerExec, null, true);
}

function NanoCheckServerExec(success, text, param)
{
	if(success == false) return;
	var data = text.split('//');
	var e = document.getElementById("nc_server_exec_infos");
	e.innerHTML = 'Exécution terminée. Vainqueur : ' + data[0];
	e = document.getElementById("nc_execlog");
	e.innerHTML = data[1];
}

function NanoLoadProgram(def)
{
	var code = prompt("Code machine", def);
	if(code == null ||code == '') return;
	var data = code.split('::');
	if(data[1] == undefined)
	{
		alert("Erreur de syntaxe");
		return;
	}
	var addr = prompt("Adresse (0-32767)", "");
	if(addr == null || addr == '') return;
	addr = parseInt(addr);
	var prgm = NanoCreateProgram(user + (n++), n-1, code, {'color':'#FF0000', 'ipcolor':'#0000FF'});
	prgm.Map(addr);
	prgm.ctx.e.SetIP(addr, true);
	nanovm.AddProgram(prgm);
}

function NanoLoadLastProgram()
{
	GetPageContent('GET', 'nano/nanovm.php?last=1', null, NanoCheckLastProgram, null, false);
}

function NanoCheckLastProgram(success, text, param)
{
	var result = '';
	if(success == false)
	{
		result = 'Erreur : impossible de se connecter au serveur';
	}
	else
	{
		result = text;
	}
	NanoLoadProgram(text);
}

function NanoResize(size)
{
	var canvas = document.getElementById('mc_nano_vm');
	var context = canvas.getContext('2d');
	if((document.body.offsetWidth < 1300 && size != 3) || size == 2)
	{
		canvas.style.width = "769px";
		canvas.style.height = "385px";
		context.canvas.width = 769;
		context.canvas.height = 385;
		canvas.size = 2;
	}
	else
	{
		canvas.style.width = "1025px";
		canvas.style.height = "513px";
		context.canvas.width = 1025;
		context.canvas.height = 513;
		canvas.size = 3;
	}
	NanoDrawGrid(canvas);
	NanoRedrawMemory(canvas);
}

function NanoDrawGrid(canvas)
{
	var context = canvas.getContext('2d');
	var width = context.canvas.width;
	var height = context.canvas.height;
	var size = canvas.size;
	context.beginPath();
	context.strokeStyle = "#DDDDDD";
	for(i = 0; i<129; i++)
	{
		context.moveTo(0, i*(size+1) + .5);
		context.lineTo(width, i*(size+1) + .5);
	}
	for(j = 0; j<257; j++)
	{
		context.moveTo(j*(size+1) + .5, 0);
		context.lineTo(j*(size+1) + .5, height);
	}
	context.stroke();
}

function NanoCreateMemory()
{
	nanovm.mem = new Array();
	for(i = 0; i<memsize; i++)
	{
		nanovm.mem[i] = {};
		nanovm.mem[i].val = 0;
		nanovm.mem[i].type = 'empty';
	}
}

function NanoReset()
{
	nanovm = {};
	n = 0;
	cycles = 0;
	NanoDrawCycles();
	if(isfight) document.location.href = document.location.href;
	else NanoInitVM();
}

function NanoRedrawMemory(canvas)
{
	for(i = 0; i<memsize; i++)
	{
		if(nanovm.mem[i].color)
			NanoDrawPixel(i, nanovm.mem[i].color);
	}
}

function NanoDrawPixel(offset, color)
{
	var canvas = document.getElementById('mc_nano_vm');
	var context = canvas.getContext('2d');
	var size = canvas.size;
	context.fillStyle = color;
	context.fillRect((offset%256)*(size+1) + 1, parseInt(offset/256)*(size+1) + 1, size, size);
}

function NanoCreateProgram(name, id, code, options)
{
	var prgm = {};
	prgm.name = name;
	prgm.id = id;
	prgm.next = null;
	prgm.prev = null;
	prgm.raw = code;
	var data = code.split('::');
	prgm.header = {};
	var header = data[0].split(':');
	for(i = 0; i < header.length; i++) { var d = header[i].split('='); prgm.header[d[0]] = d[1]; }
	prgm.code = data[1].split(':');
	prgm.options = options;
	prgm.ctx =  new list;
	prgm.currentctx = null;
	prgm.ctxcount = 0;
	prgm.Map = function(addr) { return NanoMapPrgm(this, addr); };
	prgm.CreateContext = function() { if(this.ctxcount >= maxthread) return false; var ctx = NanoCreateContext(this, this.ctxcount, this.options); this.AddContext(ctx); return ctx; }
	prgm.AddContext = function(ctx) { this.ctx.add_tail(ctx); this.ctxcount++; if(this.currentctx === null) this.currentctx = ctx; ctx.id = this.ctxcount - 1; }
	prgm.RemoveContext = function(ctx) { if(this.currentctx == ctx) this.currentctx = this.currentctx.prev; this.ctx.remove(ctx); this.ctxcount--; if(this.ctxcount == 0) { nanovm.RemoveProgram(this); ncLog(this.name + ' stopped'); } }
	prgm.GetNextContext = function() { if(this.ctxcount == 0) return false; var ctx = this.currentctx.next; this.currentctx = ctx; return ctx; }
	prgm.CreateContext();
	return prgm;
}

function NanoMapPrgm(prgm, addr)
{
	prgm.addr = addr;
	for(i = 0; i<prgm.code.length; i++)
	{
		nanovm.mem[addr + i].val = parseInt(prgm.code[i]);
		nanovm.mem[addr + i].type = 'prgm';
		nanovm.mem[addr + i].prgm = prgm.name;
		nanovm.mem[addr + i].color = prgm.options.color;
	}
	var count = prgm.code.length;
	for(i = addr; i<(addr+count); i++)
	{
		NanoDrawPixel(i, prgm.options.color);
	}
}

function NanoCreateContext(host, name, options)
{
	var context = {};
	context.reg = {};
	context.reg.ax = 0;
	context.reg.bx = 0;
	context.reg.cx = 0;
	context.reg.dx = 0;
	context.reg.fx = 0;
	context.reg.ip = 0;
	context.reg.sp = 0;
	context.reg.bp = 0;
	context.options = options;
	context.host = host;
	context.name = name;
	context.cycle = -1;
	context.next = null;
	context.prev = null;
	context.SetReg = function(reg, val) { if(reg == 'ip') this.SetIP(val); else this.reg[reg] = val; }
	context.SetIP = function(addr, draw)
	{
		if(nanovm.mem[this.reg.ip].prgm == this.host.name)
			NanoDrawPixel(this.reg.ip, this.options.color);
		this.reg.ip = addr.mod(memsize);
		if(draw)
		{
			nanovm.mem[this.reg.ip].prgm = this.host.name;
			NanoDrawPixel(this.reg.ip, this.options.ipcolor);
		}
	}
	context.IncIP = function() { this.SetIP(this.reg.ip + 1, true); }
	context.DecIP = function() { this.SetIP(this.reg.ip - 1, false); }
	context.SetFlags = function(f)
	{
		if(f & FZ) this.reg.fx |= 1;
		if(f & FNZ) this.reg.fx &= ~1;
		if(f & FC) this.reg.fx |= 2;
		if(f & FNC) this.reg.fx &= ~2;
		if(f & FP) this.reg.fx |= 4;
		if(f & FN) this.reg.fx &= ~4;
	}
	context.GetFlag = function(idx)
	{
		var flag = flags[idx - 1];
		if(flag === undefined)
		{
			NanoCrashContext(this, 'NANO_INVALID_FLAG');
			return null;
		}
		var bit = {'fz':[1,1], 'fnz':[1,0], 'fc':[2,1], 'fnc':[2,0], 'fp':[4,1], 'fn':[4,0]};
		return (this.reg.fx & bit[flag][0])==bit[flag][1];
	}
	return context;
}

function NanoStartExecution()
{
	clearInterval(timer);
	timer = setInterval('NanoClock();', 10);
}

function NanoStopExecution()
{
	clearInterval(timer);
}

function NanoEndExecution()
{
	NanoStopExecution();
	NanoDrawCycles();
	if(nanovm.callback != undefined) nanovm.callback();
}

function NanoClock()
{
	for(i = 0; i<speed; i++)
	{		
		var prgm = nanovm.GetNextProgram();
		if(prgm == false)
		{
			NanoStopExecution();
			ncLog('Execution stopped : aucun programme à exécuter');
			return;
		}
		var ctx = prgm.GetNextContext();
		if(ctx == false)
		{
			NanoStopExecution();
			ncLog('Internal error : zombie : ' + prgm.name);
			return;
		}
		NanoExecuteContext(ctx);
		if(ncdebug.dumpreg === 1)
			DebugDumpReg();
		if(NanoCheckStop() == false) { NanoEndExecution(); return; }
		if(!NanoIncCycle()) break;
	}
	NanoDrawCycles();
}

function NanoCheckStop()
{
	if(nanovm.checkstop != undefined)
		return nanovm.checkstop();
	return true;
}

function NanoIncCycle()
{
	cycles++;
	if(cycles >= cyclelimit)
	{
		NanoEndExecution();
		return false;
	}
	return true;
}

function NanoDrawCycles()
{
	var e = document.getElementById('nc_cycles');
	e.innerHTML = cycles + ' cycles';
}

function NanoExecuteContext(ctx)
{
	var inst = NanoGetNextInstruction(ctx);
	if(inst == false) return false;
	NanoExecuteInstruction(ctx, inst);
	//NanoLogExecution(ctx);
}

function NanoGetNextInstruction(ctx)
{
	var addr = (ctx.reg.ip).mod(memsize);
	if(nanovm.mem[addr].type == 'protected' && nanovm.mem[addr].protect.exec !== undefined)
	{
		NanoCrashContext(ctx, 'NANO_EXEC_PROTECTED_MEMORY');
		return false;
	}
	return nanovm.mem[addr];
}

var registers = new Array('ax', 'bx', 'cx', 'dx', 'fx', 'ip', 'sp', 'bp');
var flags = new Array('fz', 'fnz', 'fc', 'fnc', 'fp', 'fn');
var FZ = 1;
var FNZ = 2;
var FC = 4;
var FNC = 8;
var FP = 16;
var FN = 32;

function NanoGetRegister(ctx, idx)
{
	var reg = registers[idx];
	if(reg === undefined)
	{
		NanoCrashContext(ctx, 'NANO_INVALID_REGISTER');
		return false;
	}
	return reg;
}

function NanoGetMem(ctx, addr)
{
	addr = addr.mod(memsize);
	var mem = nanovm.mem[addr];
	if(mem.type == 'protected')
	{
		if(mem.protect.read !== undefined)
		{
			NanoCrashContext(ctx, 'NANO_READ_PROTECTED_MEMORY');
			return false;
		}
	}
	return mem.val;
}

function NanoGetRelMem(ctx, reladdr)
{
	var addr = (ctx.reg.ip + reladdr).mod(memsize);
	return NanoGetMem(ctx, addr);
}

function NanoSetMem(ctx, addr, val)
{
	addr = addr.mod(memsize);
	var mem = nanovm.mem[addr];
	if(mem.type == 'protected')
	{
		if(mem.protect.write !== undefined)
		{
			NanoCrashContext(ctx, 'NANO_WRITE_PROTECTED_MEMORY');
			return false;
		}
	}
	
	mem.val = val;
	mem.type = 'prgm';
	mem.prgm = ctx.host.name;
	mem.color = ctx.options.color;
	NanoDrawPixel(addr, mem.color);
	return true;
}

function NanoSetRelMem(ctx, reladdr, val)
{
	var addr = (ctx.reg.ip + reladdr).mod(memsize);
	return NanoSetMem(ctx, addr, val);
}

//{
var instructions = new Array(
	/* mov reg32, reg32 (1)*/
	function (ctx, lowh, lowl, addr)
	{
		var regd = NanoGetRegister(ctx, lowh);
		var regs = NanoGetRegister(ctx, lowl);
		if(regd === false || regs === false) return false;
		ctx.SetReg(regd, ctx.reg[regs]);
		return true;
	},
	/* mov reg32, imm32 (2)*/
	function (ctx, lowh, lowl, addr)
	{
		var regd = NanoGetRegister(ctx, lowl);
		if(regd === false) return false;
		ctx.SetReg(regd, addr);
		return true;
	},
	/* mov reg32, (reg32) (3)*/
	function (ctx, lowh, lowl, addr)
	{
		var regd = NanoGetRegister(ctx, lowh);
		var regs = NanoGetRegister(ctx, lowl);
		if(regd === false ||regs === false) return false;
		var mem = NanoGetMem(ctx, ctx.reg[regs]);
		if(mem === false) return false;
		ctx.SetReg(regd, mem);
		return true;
	},
	/* mov reg32, (addr) (4)*/
	function (ctx, lowh, lowl, addr)
	{
		var regd = NanoGetRegister(ctx, lowl);
		if(regd === false) return false;
		var mem = NanoGetMem(ctx, addr);
		if(mem === false) return false;
		ctx.SetReg(regd, mem);
		return true;
	},
	/* mov reg32, (+addr) (5)*/
	function (ctx, lowh, lowl, addr)
	{
		var regd = NanoGetRegister(ctx, lowl);
		if(regd === false) return false;
		var mem = NanoGetRelMem(ctx, addr);
		if(mem === false) return false;
		ctx.SetReg(regd, mem);
		return true;
	},
	/* mov reg32, label */
	null,
	/* mov (reg32), reg32 (7)*/
	function (ctx, lowh, lowl, addr)
	{
		var regd = NanoGetRegister(ctx, lowh);
		var regs = NanoGetRegister(ctx, lowl);
		if(regd === false ||regs === false) return false;
		return NanoSetMem(ctx, ctx.reg[regd], ctx.reg[regs]);
	},
	/* mov (addr), reg32 (8)*/
	function (ctx, lowh, lowl, addr)
	{
		var regs = NanoGetRegister(ctx, lowl);
		if(regs === false) return false;
		return NanoSetMem(ctx, addr, ctx.reg[regs]);
	},
	/* mov (+addr), reg32 (9)*/
	function (ctx, lowh, lowl, addr)
	{
		var regs = NanoGetRegister(ctx, lowl);
		if(regs === false) return false;
		return NanoSetRelMem(ctx, addr, ctx.reg[regs]);
	},
	/* add reg32, reg32 (10)*/
	function (ctx, lowh, lowl, addr)
	{
		var regd = NanoGetRegister(ctx, lowh);
		var regs = NanoGetRegister(ctx, lowl);
		if(regd === false ||regs === false) return false;
		ctx.SetReg(regd, ctx.reg[regd] + ctx.reg[regs]);
		return true;
	},
	/* add reg32, (reg32) (11)*/
	function (ctx, lowh, lowl, addr)
	{
		var regd = NanoGetRegister(ctx, lowh);
		var regs = NanoGetRegister(ctx, lowl);
		if(regd === false ||regs === false) return false;
		var mem = NanoGetMem(ctx, ctx.reg[regs]);
		if(mem === false) return false;
		ctx.SetReg(regd, ctx.reg[regd] + mem);
		return true;
	},
	/* add reg32, imm32 (12)*/
	function (ctx, lowh, lowl, addr)
	{
		var reg = NanoGetRegister(ctx, lowl);
		if(reg === false) return false;
		ctx.SetReg(reg, ctx.reg[reg] + addr);
		return true;
	},
	/* add reg32, (addr) (13)*/
	function (ctx, lowh, lowl, addr)
	{
		var reg = NanoGetRegister(ctx, lowl);
		if(reg === false) return false;
		var mem = NanoGetMem(ctx, addr);
		if(mem === false) return false;
		ctx.SetReg(reg, ctx.reg[reg] + mem);
		return true;
	},
	/* add reg32, (+addr) (14)*/
	function (ctx, lowh, lowl, addr)
	{
		var reg = NanoGetRegister(ctx, lowl);
		if(reg === false) return false;
		var mem = NanoGetRelMem(ctx, addr);
		if(mem === false) return false;
		ctx.SetReg(reg, ctx.reg[reg] + mem);
		return true;
	},
	/* sub reg32, reg32 (15)*/
	function (ctx, lowh, lowl, addr)
	{
		var regd = NanoGetRegister(ctx, lowh);
		var regs = NanoGetRegister(ctx, lowl);
		if(regd === false ||regs === false) return false;
		ctx.SetReg(regd, ctx.reg[regd] - ctx.reg[regs]);
		return true;
	},
	/* sub reg32, (reg32) (16)*/
	function (ctx, lowh, lowl, addr)
	{
		var regd = NanoGetRegister(ctx, lowh);
		var regs = NanoGetRegister(ctx, lowl);
		if(regd === false ||regs === false) return false;
		var mem = NanoGetMem(ctx, ctx.reg[regs]);
		if(mem === false) return false;
		ctx.SetReg(regd, ctx.reg[regd] - mem);
		return true;
	},
	/* sub reg32, imm32 (17)*/
	function (ctx, lowh, lowl, addr)
	{
		var reg = NanoGetRegister(ctx, lowl);
		if(reg === false) return false;
		ctx.SetReg(reg, ctx.reg[reg] - addr);
		return true;
	},
	/* sub reg32, (addr) (18)*/
	function (ctx, lowh, lowl, addr)
	{
		
		var reg = NanoGetRegister(ctx, lowl);
		if(reg === false) return false;
		var mem = NanoGetMem(ctx, addr);
		if(mem === false) return false;
		ctx.SetReg(reg, ctx.reg[reg] - mem);
		return true;
	},
	/* sub reg32, (+addr) (19)*/
	function (ctx, lowh, lowl, addr)
	{
		var reg = NanoGetRegister(ctx, lowl);
		if(reg === false) return false;
		var mem = NanoGetRelMem(ctx, addr);
		if(mem === false) return false;
		ctx.SetReg(reg, ctx.reg[reg] - mem);
		return true;
	},
	/* mul reg32, reg32 (20)*/
	function (ctx, lowh, lowl, addr)
	{
		var regd = NanoGetRegister(ctx, lowh);
		var regs = NanoGetRegister(ctx, lowl);
		if(regd === false ||regs === false) return false;
		ctx.SetReg(regd, ctx.reg[regd] * ctx.reg[regs]);
		return true;
	},
	/* mul reg32, (reg32) (21)*/
	function (ctx, lowh, lowl, addr)
	{
		var regd = NanoGetRegister(ctx, lowh);
		var regs = NanoGetRegister(ctx, lowl);
		if(regd === false ||regs === false) return false;
		var mem = NanoGetMem(ctx, ctx.reg[regs]);
		if(mem === false) return false;
		ctx.SetReg(regd, ctx.reg[regd] * mem);
		return true;
	},
	/* mul reg32, imm32 (22)*/
	function (ctx, lowh, lowl, addr)
	{
		var reg = NanoGetRegister(ctx, lowl);
		if(reg === false) return false;
		ctx.SetReg(reg, ctx.reg[reg] * addr);
		return true;
	},
	/* mul reg32, (addr) (23)*/
	function (ctx, lowh, lowl, addr)
	{
		var reg = NanoGetRegister(ctx, lowl);
		if(reg === false) return false;
		var mem = NanoGetMem(ctx, addr);
		if(mem === false) return false;
		ctx.SetReg(reg, ctx.reg[reg] - mem);
		return true;
	},
	/* mul reg32, (+addr) (24)*/
	function (ctx, lowh, lowl, addr)
	{
		var reg = NanoGetRegister(ctx, lowl);
		if(reg === false) return false;
		var mem = NanoGetRelMem(ctx, addr);
		if(mem === false) return false;
		ctx.SetReg(reg, ctx.reg[reg] * mem);
		return true;
	},
	/* inc reg32 (25)*/
	function (ctx, lowh, lowl, addr)
	{
		var reg = NanoGetRegister(ctx, lowl);
		if(reg === false) return false;
		ctx.SetReg(reg, ctx.reg[reg] + 1);
		return true;
	},
	/* inc (reg32) (26)*/
	function (ctx, lowh, lowl, addr)
	{
		var reg = NanoGetRegister(ctx, lowl);
		if(reg === false) return false;
		var mem = NanoGetMem(ctx, ctx.reg[reg]);
		if(mem === false) return false;
		return NanoSetRelMem(ctx, ctx.reg[reg], mem + 1);
	},
	/* dec reg32 (27)*/
	function (ctx, lowh, lowl, addr)
	{
		var reg = NanoGetRegister(ctx, lowl);
		if(reg === false) return false;
		ctx.SetReg(reg, ctx.reg[reg] - 1);
		return true;
	},
	/* dec (reg32) (28)*/
	function (ctx, lowh, lowl, addr)
	{
		var reg = NanoGetRegister(ctx, lowl);
		if(reg === false) return false;
		var mem = NanoGetMem(ctx, ctx.reg[reg]);
		if(mem === false) return false;
		return NanoSetRelMem(ctx, ctx.reg[reg], mem - 1);
	},
	/* or reg32, reg32 (29)*/
	function (ctx, lowh, lowl, addr)
	{
		var regd = NanoGetRegister(ctx, lowh);
		var regs = NanoGetRegister(ctx, lowl);
		if(regd === false ||regs === false) return false;
		ctx.SetReg(regd, ctx.reg[regd] | ctx.reg[regs]);
		return true;
	},
	/* or reg32, (reg32) (30)*/
	function (ctx, lowh, lowl, addr)
	{
		var regd = NanoGetRegister(ctx, lowh);
		var regs = NanoGetRegister(ctx, lowl);
		if(regd === false ||regs === false) return false;
		var mem = NanoGetMem(ctx, ctx.reg[regs]);
		if(mem === false) return false;
		ctx.SetReg(regd, ctx.reg[regd] | mem);
		return true;
	},
	/* or reg32, imm32 (31)*/
	function (ctx, lowh, lowl, addr)
	{
		var reg = NanoGetRegister(ctx, lowl);
		if(reg === false) return false;
		ctx.SetReg(reg, ctx.reg[reg] | addr);
		return true;
	},
	/* or reg32, (addr) (32)*/
	function (ctx, lowh, lowl, addr)
	{
		var reg = NanoGetRegister(ctx, lowl);
		if(reg === false) return false;
		var mem = NanoGetMem(ctx, addr);
		if(mem === false) return false;
		ctx.SetReg(reg, ctx.reg[reg] | mem);
		return true;
	},
	/* or reg32, (+addr) (33)*/
	function (ctx, lowh, lowl, addr)
	{
		var reg = NanoGetRegister(ctx, lowl);
		if(reg === false) return false;
		var mem = NanoGetRelMem(ctx, addr);
		if(mem === false) return false;
		ctx.SetReg(reg, ctx.reg[reg] | mem);
		return true;
	},
	/* and reg32, reg32 (34)*/
	function (ctx, lowh, lowl, addr)
	{
		var regd = NanoGetRegister(ctx, lowh);
		var regs = NanoGetRegister(ctx, lowl);
		if(regd === false ||regs === false) return false;
		ctx.SetReg(regd, ctx.reg[regd] & ctx.reg[regs]);
		return true;
	},
	/* and reg32, (reg32) (35)*/
	function (ctx, lowh, lowl, addr)
	{
		var regd = NanoGetRegister(ctx, lowh);
		var regs = NanoGetRegister(ctx, lowl);
		if(regd === false ||regs === false) return false;
		var mem = NanoGetMem(ctx, ctx.reg[regs]);
		if(mem === false) return false;
		ctx.SetReg(regd, ctx.reg[regd] & mem);
		return true;
	},
	/* and reg32, imm16 (36)*/
	function (ctx, lowh, lowl, addr)
	{
		var reg = NanoGetRegister(ctx, lowl);
		if(reg === false) return false;
		ctx.SetReg(reg, ctx.reg[reg] & addr);
		return true;
	},
	/* and reg32, (addr) (37)*/
	function (ctx, lowh, lowl, addr)
	{
		var reg = NanoGetRegister(ctx, lowl);
		if(reg === false) return false;
		var mem = NanoGetMem(ctx, addr);
		if(mem === false) return false;
		ctx.SetReg(reg, ctx.reg[reg] & mem);
		return true;
	},
	/* and reg32, (+addr) (38)*/
	function (ctx, lowh, lowl, addr)
	{
		var reg = NanoGetRegister(ctx, lowl);
		if(reg === false) return false;
		var mem = NanoGetRelMem(ctx, addr);
		if(mem === false) return false;
		ctx.SetReg(reg, ctx.reg[reg] & mem);
		return true;
	},
	/* xor reg32, reg32 (39)*/
	function (ctx, lowh, lowl, addr)
	{
		var regd = NanoGetRegister(ctx, lowh);
		var regs = NanoGetRegister(ctx, lowl);
		if(regd === false ||regs === false) return false;
		ctx.SetReg(regd, ctx.reg[regd] ^ ctx.reg[regs]);
		return true;
	},
	/* xor reg32, (reg32) (40)*/
	function (ctx, lowh, lowl, addr)
	{
		var regd = NanoGetRegister(ctx, lowh);
		var regs = NanoGetRegister(ctx, lowl);
		if(regd === false ||regs === false) return false;
		var mem = NanoGetMem(ctx, ctx.reg[regs]);
		if(mem === false) return false;
		ctx.SetReg(regd, ctx.reg[regd] ^ mem);
		return true;
	},
	/* xor reg32, imm32 (41)*/
	function (ctx, lowh, lowl, addr)
	{
		var reg = NanoGetRegister(ctx, lowl);
		if(reg === false) return false;
		ctx.SetReg(reg, ctx.reg[reg] ^ addr);
		return true;
	},
	/* xor reg32, (addr)(42)*/
	function (ctx, lowh, lowl, addr)
	{
		var reg = NanoGetRegister(ctx, lowl);
		if(reg === false) return false;
		var mem = NanoGetMem(ctx, addr);
		if(mem === false) return false;
		ctx.SetReg(reg, ctx.reg[reg] ^ mem);
		return true;
	},
	/* xor reg32, (+addr) (43)*/
	function (ctx, lowh, lowl, addr)
	{
		var reg = NanoGetRegister(ctx, lowl);
		if(reg === false) return false;
		var mem = NanoGetRelMem(ctx, addr);
		if(mem === false) return false;
		ctx.SetReg(reg, ctx.reg[reg] ^ mem);
		return true;
	},
	/* push reg32 (44)*/
	function (ctx, lowh, lowl, addr)
	{
		var reg = NanoGetRegister(ctx, lowl);
		if(reg === false) return false;
		NanoSetMem(ctx, ctx.reg.sp, ctx.reg[reg]);
		ctx.reg.sp++;
		return true;
	},
	/* pop reg32 (45)*/
	function (ctx, lowh, lowl, addr)
	{
		var reg = NanoGetRegister(ctx, lowl);
		if(reg === false) return false;
		if(ctx.reg.sp == ctx.reg.bp)
			NanoCrashContext(ctx, 'NANO_POP_EMPTY_STACK');
		else
		{
			ctx.reg.sp--;
			var val = NanoGetMem(ctx, ctx.reg.sp);
			ctx.SetReg(reg, val);
		}
		return true;
	},
	/* call reg32 (46)*/
	function (ctx, lowh, lowl, addr)
	{
		var reg = NanoGetRegister(ctx, lowl);
		if(reg === false) return false;
		NanoSetMem(ctx, ctx.reg.sp, ctx.reg.ip + 1);
		ctx.reg.sp++;
		ctx.SetIP((ctx.reg[reg] - 1).mod(memsize), false);
		return true;
	},
	/* call (reg32) (47)*/
	null,
	/* call addr (48)*/
	function (ctx, lowh, lowl, addr)
	{
		NanoSetMem(ctx, ctx.reg.sp, ctx.reg.ip + 1);
		ctx.reg.sp++;
		ctx.SetIP((addr-1).mod(memsize), false);
		return true;
	},
	/* call +addr (49)*/
	function (ctx, lowh, lowl, addr)
	{
		NanoSetMem(ctx, ctx.reg.sp, ctx.reg.ip + 1);
		ctx.reg.sp++;
		ctx.SetIP((ctx.reg.ip+addr-1).mod(memsize), false);
		return true;
	},
	/* call label (50)*/
	function (ctx, lowh, lowl, addr)
	{
		if(functions[lowl] != undefined)
			if(functions[lowl](ctx) == false)
				ctx.DecIP();
		return true;
	},
	/* call (addr) (51)*/
	null,
	/* call (+addr) (52)*/
	null,
	/* call flag, reg32 (53)*/
	function (ctx, lowh, lowl, addr)
	{
		var reg = NanoGetRegister(ctx, lowh);
		var flag = ctx.GetFlag(lowl);
		if(flag === null || reg === null) return false;
		if(flag)
		{
			NanoSetMem(ctx, ctx.reg.sp, ctx.reg.ip + 1);
			ctx.reg.sp++;
			ctx.SetIP((ctx.reg[reg] - 1).mod(memsize), false);
		}
		return true;
	},
	/* call flag, (reg32) (54)*/
	null,
	/* call flag, addr (55)*/
	function (ctx, lowh, lowl, addr)
	{
		var flag = ctx.GetFlag(lowl);
		if(flag === null) return false;
		if(flag)
		{
			NanoSetMem(ctx, ctx.reg.sp, ctx.reg.ip + 1);
			ctx.reg.sp++;
			ctx.SetIP((addr-1).mod(memsize), false);
		}
		return true;
	},
	/* call flag, +addr (56)*/
	function (ctx, lowh, lowl, addr)
	{
		var flag = ctx.GetFlag(lowl);
		if(flag === null) return false;
		if(flag)
		{
			NanoSetMem(ctx, ctx.reg.sp, ctx.reg.ip + 1);
			ctx.reg.sp++;
			ctx.SetIP((ctx.reg.ip+addr-1).mod(memsize), false);
		}
		return true;
	},
	/* call flag, label (57)*/
	null,
	/* call flag, (addr) (58)*/
	null,
	/* call flag, (+addr) (59)*/
	null,
	/* ret (60)*/
	function (ctx, lowh, lowl, addr)
	{
		if(ctx.reg.sp == ctx.reg.bp)
			NanoCrashContext(ctx, 'NANO_POP_EMPTY_STACK');
		else
		{
			ctx.reg.sp--;
			var val = NanoGetMem(ctx, ctx.reg.sp);
			ctx.SetIP(val - 1, false);
		}
		return true;
	},
	/* ret flag (61)*/
	function (ctx, lowh, lowl, addr)
	{
		var flag = ctx.GetFlag(lowl);
		if(flag === null) return false;
		if(flag)
		{
			if(ctx.reg.sp == ctx.reg.bp)
				NanoCrashContext(ctx, 'NANO_POP_EMPTY_STACK');
			else
			{
				ctx.reg.sp--;
				var val = NanoGetMem(ctx, ctx.reg.sp);
				ctx.SetIP(val - 1, false);
			}
		}
		return true;
	},
	/* jmp addr (62)*/
	function (ctx, lowh, lowl, addr)
	{
		ctx.SetIP(addr - 1, false);
		return true;
	},
	/* jmp +addr (63)*/
	function (ctx, lowh, lowl, addr)
	{
		ctx.SetIP(ctx.reg.ip + addr - 1, false);
		return true;
	},
	/* jmp label (64)*/
	function (ctx, lowh, lowl, addr)
	{
		NanoCrashContext(ctx, 'NANO_BAD_OP_CODE');
		return false;
	},
	/* jmp flag, addr (65)*/
	function (ctx, lowh, lowl, addr)
	{
		var flag = ctx.GetFlag(lowl);
		if(flag === null) return false;
		if(flag) ctx.SetIP(addr - 1, false);
		return true;
	},
	/* jmp flag, +addr (66)*/
	function (ctx, lowh, lowl, addr)
	{
		var flag = ctx.GetFlag(lowl);
		if(flag === null) return false;
		if(flag) ctx.SetIP(ctx.reg.ip + addr - 1, false);
		return true;
	},
	/* jmp flag, label (67)*/
	null,
	/* cmp reg32, reg32 (68)*/
	function (ctx, lowh, lowl, addr)
	{
		var regd = NanoGetRegister(ctx, lowh);
		var regs = NanoGetRegister(ctx, lowl);
		if(regd === false || regs === false) return false;
		NanoCmp(ctx, ctx.reg[regd], ctx.reg[regs]);
		return true;
	},
	/* cmp reg32, imm32 (69)*/
	function (ctx, lowh, lowl, addr)
	{
		var regd = NanoGetRegister(ctx, lowl);
		if(regd === false) return false;
		NanoCmp(ctx, ctx.reg[regd], addr);
		return true;
	},
	/* test reg32 (70)*/
	function (ctx, lowh, lowl, addr)
	{
		var regd = NanoGetRegister(ctx, lowl);
		if(regd === false) return false;
		NanoCmp(ctx, ctx.reg[regd], 0);
		return true;
	},
	/* jmp reg32 (71)*/
	function (ctx, lowh, lowl, addr)
	{
		var regs = NanoGetRegister(ctx, lowl);
		if(regs === false) return false;
		ctx.SetIP(ctx.reg[regs] - 1, false);
		return true;
	},
	/* jmp flag, reg32 (72)*/
	function (ctx, lowh, lowl, addr)
	{
		var flag = ctx.GetFlag(lowh);
		var regs = NanoGetRegister(ctx, lowl);
		if(flag === null || regs === false) return false;
		if(flag) ctx.SetIP(ctx.reg[regs] - 1, false);
		return true;
	},
	/* djnz addr (73)*/
	function (ctx, lowh, lowl, addr)
	{
		ctx.reg['cx']--;
		if(ctx.reg['cx'] == 0) return true;
		ctx.SetIP(addr - 1, false);
		return true;
	},
	/* djnz +addr (74)*/
	function (ctx, lowh, lowl, addr)
	{
		ctx.reg['cx']--;
		if(ctx.reg['cx'] == 0) return true;
		ctx.SetIP(ctx.reg['ip'] + addr - 1, false);
		return true;
	},
	/* djnz label (75)*/
	null,
	/* mov reg32, +addr (76)*/
	function (ctx, lowh, lowl, addr)
	{
		var regs = NanoGetRegister(ctx, lowl);
		if(regs === false) return false;
		ctx.SetReg(regs, ctx.reg['ip'] + addr);
		return true;
	},
	/* mov reg32, label (77)*/
	null,
	/* shr reg32 (78)*/
	function (ctx, lowh, lowl, addr)
	{
		var regs = NanoGetRegister(ctx, lowl);
		if(regs === false) return false;
		var carry = ctx.reg[regs] & 1;
		ctx.SetReg(regs, ctx.reg[regs] >> 1);
		if(carry) ctx.SetFlags(FC);
		else ctx.SetFlags(FNC);
	},
	/* shl reg32 (79)*/
	function (ctx, lowh, lowl, addr)
	{
		var regs = NanoGetRegister(ctx, lowl);
		if(regs === false) return false;
		var carry = ctx.reg[regs] & 0x80000000;
		ctx.SetReg(regs, ctx.reg[regs] << 1);
		if(carry) ctx.SetFlags(FC);
		else ctx.SetFlags(FNC);
	});
//}
var functions = new Array(
	/* CreateThread ax=addr*/
	function (ctx)
	{
		if(ctx.cycle < 0) ctx.cycle = 10;
		ctx.cycle--;
		if(ctx.cycle == 0)
		{
			var newctx = ctx.host.CreateContext();
			if(newctx !== false)
				newctx.SetIP(ctx.reg.ax);
		}
		return ctx.cycle==-1;
	},
	/* DebugDispReg ax=reg */
	function (ctx)
	{
		return 1;
	});

function NanoCmp(ctx, a, b)
{
	if(a == b) ctx.SetFlags(FZ|FP);
	else if(a - b > 0) ctx.SetFlags(FNZ|FP);
	else ctx.SetFlags(FNZ|FN);
}

function NanoExecuteInstruction(ctx, inst)
{
	var val = inst.val;
	var op = ((val&0xFF000000) >> 24) - 1;
	var lowh = (val&0xF00000) >> 20;
	var lowl = (val&0xF0000) >> 16;
	var addr = val&0xFFFF;
	if(addr&0x8000) addr |= 0xFFFF0000;
	if(instructions[op] === undefined || instructions[op] === null) { NanoCrashContext(ctx, 'NANO_BAD_OP_CODE'); return false; }
	instructions[op](ctx, lowh, lowl, addr);
	ctx.IncIP();
}

function NanoCrashContext(ctx, error)
{
	ctx.host.RemoveContext(ctx);
	ncLog('Crash : ' + ctx.host.name + '.thread' + ctx.name + '\n' + error + '\n' + cycles + ' cycles' + '\n' + ctx.reg.ip);
}

function NanoLogExecution(ctx)
{
	var e = document.getElementById('out');
	e.innerHTML += ctx.host.name + '.thread' + ctx.name + '<br />';
}

function NanoIncSpeed()
{
	speed++;
	NanoUpdateSpeed();
}

function NanoDecSpeed()
{
	speed--;
	if(speed == 0) speed = 1;
	NanoUpdateSpeed();
}

function NanoUpdateSpeed()
{
	var e = document.getElementById("mc_nvm_speed");
	e.innerHTML = speed;
}

function DebugDisplayRegisters(m)
{
	if(ncdebug.dumpreg === 1)
	{
		ncdebug.dumpreg = undefined;
		document.getElementById("nc_debug_reg").innerHTML = '';
		m.innerHTML = 'Afficher les registres';
	}
	else
	{
		ncdebug.dumpreg = 1;
		DebugDumpReg();
		m.innerHTML = 'Masquer les registres';
	}
}

function DebugDumpReg()
{
	var out = {};
	out.v = '';
	nanovm.prgm.foreach(function(p, out)
		{
			out.v += '<table class="mc_table nc_dreg_table"><tr><th colspan=' + p.ctxcount*2 + '>' + p.name + '</th></tr><tr>';
			p.ctx.foreach(function(c, out) { out.v += '<td colspan=2>' + c.name + '</td>'; }, out);
			out.v += '</tr>';
			for(i = 0; i<8; i++)
			{
				out.v += '<tr>';
				p.ctx.foreach(function(c, out) { out.v += '<td>' + registers[i] + '</td><td>' + c.reg[registers[i]] + '</td>'; }, out);
				out.v += '</tr>';
			}
			p.ctx.foreach(function(c, out) { out.v += '<td>mem[ip]</td><td>' + nanovm.mem[c.reg['ip']].val + '</td>'; }, out);
			out.v += '</table>';
		}, out);
	var e = document.getElementById("nc_debug_reg");
	e.innerHTML = out.v;
}